package com.qinchen.springmvc.servlet;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.qinchen.springmvc.annotation.RequestParam;
import com.qinchen.springmvc.annotation.Controller;
import com.qinchen.springmvc.annotation.RequestMapping;
import com.qinchen.springmvc.annotation.ResponseBody;
import com.qinchen.springmvc.contenxt.WebApplicationContext;
import com.qinchen.springmvc.handler.MyHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author qinchen
 * @date 2021/1/16 17:50
 * 向未来的自己致敬
 * 描述：XXX
 */
public class DispatcherServlet extends HttpServlet {

    private WebApplicationContext webApplicationContext;

    //创建集合  用于存放  映射关系    映射地址  与  控制器.方法，用于发送请求直接从该集合中进行匹配
    List<MyHandler> handList = new ArrayList<>();

    @Override
    public void init() throws ServletException {
        // 1、加载初始化参数   classpath:springmvc.xml
        String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");

        // 2、创建Springmvc容器
        webApplicationContext = new WebApplicationContext(contextConfigLocation);

        // 3、进行初始化操作
        webApplicationContext.onRefresh();

        // 4、初始化请求映射关系
        initHandlerMapping();
    }

    private void initHandlerMapping() {
        // 变量容器中所有的bean
        for (Map.Entry<String, Object> entry : webApplicationContext.iocMap.entrySet()) {
            // 获取bean的class类型
            Class<?> clazz = entry.getValue().getClass();
            // 判断该bean是否被Controller注解修饰
            if (clazz.isAnnotationPresent(Controller.class)) {
                // 获取bean中所有的方法，为这些方法建立映射关系
                Method[] methods = clazz.getDeclaredMethods();
                // 遍历所有的methods
                for (Method method : methods) {
                    // 如果方法上存在RequestMapping注解
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                        // 获取注解中的值
                        String url = requestMapping.value();
                        // 建立 映射地址 与 控制器.方法
                        MyHandler myHandler = new MyHandler(url, entry.getValue(), method);
                        handList.add(myHandler);
                    }
                }
            }
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 进行请求分发处理
        doDispatcher(req, resp);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    //进行请求分发处理
    public void doDispatcher(HttpServletRequest req, HttpServletResponse resp) {

        // 根据用户的请求地址
        MyHandler myHandler = getHandler(req);

        try {
            // 没有找到，打印404信息
            if (myHandler == null) {
                resp.getWriter().print("<h1>404 NOT FOUND!</h1>");
            } else {
                // 调用处理方法之前 进行参数的注入

                Parameter[] parameters = myHandler.getMethod().getParameters();
                List<Object> params = new ArrayList<>();
                for (Parameter parameter : parameters) {
                    // 如果参数上加了RequestParam注解
                    RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
                    if (null != requestParam) {
                        String paramValue = requestParam.value();
                        String param = req.getParameter(paramValue);

                        if (param != null && parameter.getType().equals(Long.class)) {
                            // 对参数类型进行转换
                            params.add(new Long(param));
                        } else {
                            params.add(param);
                        }
                    } else {
                        params.add(null);
                    }
                }

                // 反射，调用目标方法
                Object result = myHandler.getMethod().invoke(myHandler.getController(), params.toArray());

                if (result instanceof String) {
                    // 跳转JSP
                    String viewName = (String) result;
                    // forward:/success.jsp
                    if (viewName.contains(":")) {
                        String viewType = viewName.split(":")[0];
                        String viewPage = viewName.split(":")[1];
                        if (viewType.equals("forward")) {
                            req.getRequestDispatcher(viewPage).forward(req, resp);
                        } else {
                            // redirect:/user.jsp
                            resp.sendRedirect(viewPage);
                        }
                    } else {
                        //默认就转发
                        req.getRequestDispatcher(viewName).forward(req, resp);
                    }
                } else {
                    // 返回JSON格式数据
                    Method method = myHandler.getMethod();
                    if (method.isAnnotationPresent(ResponseBody.class)) {
                        // 将返回值转换成 json格式数据
                        ObjectMapper objectMapper = new ObjectMapper();
                        String json = objectMapper.writeValueAsString(result);
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter writer = resp.getWriter();
                        writer.print(json);
                        writer.flush();
                        writer.close();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据用户请求，查找到对应的handler
     *
     * @param req
     * @return
     */
    private MyHandler getHandler(HttpServletRequest req) {
        // 获取到当前请求的URL
        String requestURI = req.getRequestURI();
        for (MyHandler myHandler : handList) {
            // 从容器的Handle取出URL 和 用户的请求地址进行匹配，找到满足条件的Handler
            if (myHandler.getUrl().equals(requestURI)) {
                return myHandler;
            }
        }
        return null;
    }
}
